/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 actor.h

	$Header: /heroes4/actor.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "actor.h"

#include "adv_actor_action.h"
#include "adv_actor_model.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "direction.h"
#include "float_object.h"
#include "game_time.h"
#include "pixel_24.h"

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
t_actor::t_actor()
       : m_start_time( get_time() ),
		m_frame_delay( 100 ),
		m_frame_count( 1 ),
		m_frame( 0 ),
		m_frame_offset( 0, 0 ),
		m_direction( k_direction_east ),
		m_action( k_adv_actor_action_wait )
{
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::do_animates() const
{
	return get_model().get_frame_count( m_action, m_direction ) > 1;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::has_action( t_adv_actor_action_id action ) const
{
	t_model const& model = get_model();

	return model.has_action( action, m_direction );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::set_action( t_adv_actor_action_id sequence )
{
	////////////////////////////////////////////////////////////
	// functionality

	if (m_map!= NULL)
		m_map->record_set_action_event( this, sequence );
	
	////////////////////////////////////////////////////////////
	
	t_model const& model = get_model();

	m_current_sequence = model.get_sequence_cache( sequence, m_direction ).get();
	on_move_begin();
	m_action = sequence;
	m_frame_count = model.get_frame_count( sequence, m_direction );
	m_frame_delay = 1000 / model.get_frames_per_second( sequence );
	m_start_time = get_time();
	m_frame = 0;
	on_move_end();
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::on_model_changed()
{
	t_model const& model = get_model();

	t_abstract_adv_actor::on_model_changed();
	set_action( m_action );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::set_direction( t_direction direction )
{
	t_model const& model = get_model();

	on_move_begin();
	m_direction = direction;
	m_current_sequence = model.get_sequence_cache( m_action, m_direction ).get();
	m_frame_count = model.get_frame_count( m_action, m_direction );
	if (m_frame_count <= m_frame)
		m_frame = m_frame_count - 1;
	m_start_time = get_time();
	on_move_end();
}


// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::do_visible_through_obstacles() const
{
	return true;
}


// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::draw_shadow_to( t_uint32              current_time, 
                              t_screen_rect const&  source_rect,
                              t_abstract_bitmap16&  dest, 
				              t_screen_point const& dest_point ) const
{
	t_model const& model = get_model();

	model.draw_shadow_to( m_action, m_direction, get_frame( current_time ), 
		                  source_rect - m_frame_offset, dest, dest_point + m_frame_offset );

}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::draw_shadow_to( t_uint32              current_time,
		                      t_abstract_bitmap16&  dest, 
		                      t_screen_point const& dest_point ) const
{
	t_model const& model = get_model();

	model.draw_shadow_to( m_action, m_direction, get_frame( current_time ), dest,
		                  dest_point + m_frame_offset );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::do_draw_to( t_uint32              current_time, 
	                      t_screen_rect const&  source_rect,
	                      t_abstract_bitmap16&  dest, 
					      t_screen_point const& dest_point,
						  int					alpha ) const
{
	t_model const& model = get_model();

	model.draw_to( m_action, m_direction, get_frame( current_time ), 
		           source_rect - m_frame_offset, dest, dest_point + m_frame_offset, alpha );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
void t_actor::do_draw_to( t_uint32              current_time, 
	                      t_abstract_bitmap16&  dest, 
	                      t_screen_point const& dest_point ) const
{
	t_model const& model = get_model();

	model.draw_to( m_action, m_direction, get_frame( current_time ), dest, 
		           dest_point + m_frame_offset );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
t_screen_rect t_actor::do_get_rect() const
{
	t_model const& model = get_model();
	t_screen_rect  result = model.get_rect( m_action, m_direction );

	return result + m_frame_offset;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
t_screen_rect t_actor::do_get_rect( t_uint32 current_time ) const
{
	t_model const& model  = get_model();
	t_screen_rect  result = model.get_rect( m_action, m_direction, get_frame( current_time ) );
	
	return result + m_frame_offset;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
t_screen_rect t_actor::get_shadow_rect() const
{
	t_model const& model = get_model();
	t_screen_rect  result = model.get_shadow_rect( m_action, m_direction );

	return result + m_frame_offset;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
t_screen_rect t_actor::get_shadow_rect( t_uint32 current_time ) const
{
	t_model const& model = get_model();
	t_screen_rect  result = model.get_shadow_rect( m_action, m_direction, 
		                                           get_frame( current_time ) );

	return result + m_frame_offset;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::do_hit_test( t_uint32 current_time, t_screen_point const & arg ) const
{
	t_model const& model = get_model();
	t_screen_point point = arg - m_frame_offset;

	return model.hit_test( m_action, m_direction, get_frame( current_time ), point );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::do_needs_redrawing( t_uint32 last_update_time, 
                                  t_uint32 current_time ) const
{
	return get_frame( last_update_time ) != get_frame( current_time );
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
int t_actor::get_frame( t_uint32 current_time ) const
{
	if (m_action == k_adv_actor_action_wait)
		return ((current_time - m_start_time) / m_frame_delay) % m_frame_count;
	return m_frame;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// turn actor without moving.
// -------------------------------------------------------------
void t_actor::turn_to( t_direction direction )
{
	on_move_begin();
	m_frame_offset.x = 0;
	m_frame_offset.y = 0;
	set_direction( direction );
	on_move_end();
}


// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::write_object( std::streambuf& stream ) const
{
	put<bool>( stream, true );  // mobile flag.  Should not be read in "read()", it's part of 
	                            // the header.
	put<int>( stream, m_start_time );
	put<int>( stream, m_frame_delay );
	put<t_uint16>( stream, m_frame_count );
	put<t_uint16>( stream, m_frame );
	put<t_screen_point>( stream, m_frame_offset );
	put<t_direction>( stream, m_direction );
	put<t_adv_actor_action_id>( stream, m_action );
	return true;
}

// -------------------------------------------------------------
// animated, mobile object for adventure map
// -------------------------------------------------------------
bool t_actor::read( std::streambuf& stream, int version ) 
{
	if (version == 0)
		get<int>( stream );
	m_start_time		= get<int>( stream );
	m_frame_delay		= get<int>( stream );
	m_frame_count		= get<t_uint16>( stream );
	m_frame				= get<t_uint16>( stream );
	m_frame_offset		= get<t_screen_point>( stream );
	if (version == 0)
		get<t_screen_point>( stream );
	m_direction			= get<t_direction>( stream );
	m_action			= get<t_adv_actor_action_id>( stream );
	return true;
}

void t_actor::set_frame_offset( t_screen_point const& offset )
{
	on_move_begin();
	m_frame_offset = offset;
	on_move_end();
}

void t_actor::set_frame( int frame )
{
	on_move_begin();
	m_frame = frame;
	on_move_end();
}

t_screen_point t_actor::get_frame_offset() const
{
	return m_frame_offset;
}

bool t_actor::is_actor() const
{
	return true;
}

